﻿using Dapper;
using Microsoft.Extensions.Options;
using Oracle.ManagedDataAccess.Client;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Linq;
using System.Threading.Tasks;

namespace Xman.Dapper
{
    public class DapperContext
    {
        private readonly DapperContextOptions _options;
        public DapperContext(IOptions<DapperContextOptions> options)
        {
            _options = options.Value;
        }
        public async Task<IDbConnection> OpenConnectionAsync()
        {
            DbConnection connection = _options.DbType switch
            {
                EnumDbType.Sqlserver => new SqlConnection(_options.Connstr),
                EnumDbType.Oracle => new OracleConnection(_options.Connstr),
                _ => throw new Exception("未知的数据库类型")
            };
            await connection.OpenAsync();
            return connection;
        }

        public async Task<int> ExcuteCommandAsync(string sqlString, object entityParameter = null, IDbTransaction transaction = null, CommandType? commandType = null)
        {
            if (transaction != null)
            {
                return await transaction.Connection.ExecuteAsync(sqlString, entityParameter, transaction, commandType: commandType);
            }
            using var connection = await OpenConnectionAsync();
            return await connection.ExecuteAsync(sqlString, entityParameter, commandType: commandType);
        }

        public async Task<int> ExecuteScalarAsync(string sqlString, object entityParameter = null, IDbTransaction transaction = null, CommandType? commandType = null)
        {
            if (transaction != null)
            {
                return await transaction.Connection.ExecuteScalarAsync<int>(sqlString, entityParameter, transaction, commandType: commandType);
            }
            using var connection = await OpenConnectionAsync();
            return await connection.ExecuteScalarAsync<int>(sqlString, entityParameter, commandType: commandType);
        }

        public async Task<IEnumerable<T>> GetListAsync<T>(string sqlString, object entityParameter = null, IDbTransaction transaction = null)
        {
            if (transaction != null)
            {
                return await transaction.Connection.QueryAsync<T>(sqlString, entityParameter, transaction);
            }
            using var connection = await OpenConnectionAsync();
            return await connection.QueryAsync<T>(sqlString, entityParameter);
        }

        public async Task<T> GetFirstOrDefaultAsync<T>(string sqlString, object entityParameter = null, IDbTransaction transaction = null)
        {
            if (transaction != null)
            {
                return await transaction.Connection.QueryFirstOrDefaultAsync<T>(sqlString, entityParameter, transaction);
            }
            using var connection = await OpenConnectionAsync();
            return await connection.QueryFirstOrDefaultAsync<T>(sqlString, entityParameter, transaction);
        }

        public async Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TReturn>(string sql, Func<TFirst, TSecond, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
        {
            if (transaction != null)
            {
                return await transaction.Connection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
            }
            using var connection = await OpenConnectionAsync();
            return await connection.QueryAsync(sql, map, param, null, buffered, splitOn, commandTimeout, commandType);
        }

        public async Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TReturn>(string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
        {
            if (transaction != null)
            {
                return await transaction.Connection.QueryAsync(sql, map, param, transaction, buffered, splitOn, commandTimeout, commandType);
            }
            using var connection = await OpenConnectionAsync();
            return await connection.QueryAsync(sql, map, param, null, buffered, splitOn, commandTimeout, commandType);
        }

        public async Task ExcuteTransAsync(List<TranModel> list)
        {
            using var connection = await OpenConnectionAsync();
            await using var tran = await ((DbConnection)connection).BeginTransactionAsync();
            var method = "";
            var result = 0;
            try
            {
                foreach (var t in list)
                {
                    method = t.MethodName;
                    await t.Func(tran, result);
                }

                await tran.CommitAsync();
            }
            catch (Exception ex)
            {
                await tran.RollbackAsync();
                throw new Exception($"异常：方法名-{method}，信息：{ex}");
            }
        }

    }
}
